package everything;

import everything.search.GreedySearchAgent;
import everything.search.RTASearchAgent;
import everything.search.SearchAgent;
import graphics.client.Client;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.Vector;

import javax.swing.JOptionPane;

public class WorldMap {
	public static final int EMPTY = 0;
	public static final int WALL = 1;
	//	public static final int AGENT = 2;
	public static final int ICE = 3;
	//	public static final int THREAT = 4;
	public static final int FLAG = 5;
	public static final int OUTOFBOUNDS = 6;


	protected int[][] _board;
	protected Client _client;
	protected Set<Agent> _agents;
	protected List<int[]> _flagList;
	protected List<Automaton> _sentries;
	protected List<SearchAgent> _searchAgents;
	protected Queue<Agent> _queue;
	protected int _flagValues;
	protected int _delay;

	public WorldMap(String filename, int flagValues,int delay){
		_board = readFromFile(filename);
		_agents = new HashSet<Agent>();
		_client = new Client();
		_flagList = new LinkedList<int[]>();
		_sentries = new LinkedList<Automaton>();
		_searchAgents = new LinkedList<SearchAgent>();
		_queue = new LinkedList<Agent>();
		_flagValues = flagValues;
		_delay = delay;
		try {
			String walls = "Walls";
			String flags = "Flags";
			String ice = "Ice";
			for (int i=0; i< _board[0].length; i++){
				for (int j=0; j< _board.length; j++){
					if (_board[j][i] == WALL){
						walls = walls + " " + getCellNumber(j, i);
					} else if(_board[j][i] == FLAG){
						flags = flags + " " + getCellNumber(j, i);
						_flagList.add(new int[]{j,i});
					} else if(_board[j][i] == ICE){
						ice = ice + " " + getCellNumber(j, i);
					}
				}
			}
			_client.sendMessage("Board " + _board[0].length + " " + _board.length );
			_client.sendMessage(walls);
			_client.sendMessage(flags);
			_client.sendMessage(ice);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 *
	 * @param x row
	 * @param y column
	 * @return
	 */
	private int getCellNumber(int x, int y){
		return (y+1+x*_board[0].length);
	}

	// adds agent to the set and map
	public void addAgent(Agent agent){
		_agents.add(agent);
		try {
			_client.sendMessage("Agent " + agent.getName() + " " + agent.getColor() + " " + agent.getOrientation() + " " + getCellNumber(agent.getX(),agent.getY()));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void moveAgent(Agent agent, String direction){
		try {
			_client.sendMessage("MoveAgent " + agent.getName() + " " + direction);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void turnAgent(Agent agent, String orientation) {
		try {
			_client.sendMessage("TurnAgent " + agent.getName() + " " + orientation);
		} catch (IOException e) {
			e.printStackTrace();
		}

	}


	private int[][] readFromFile(String filename){
		try {
			BufferedReader In = new BufferedReader(new InputStreamReader(new FileInputStream(filename)));
			Vector<int[]> lines = new Vector<int[]>();
			String line;
			while ((line=In.readLine())!=null){
				int[] tmp = new int[line.length()];
				for (int i=0; i<line.length(); i++){
					char ch = line.charAt(i);
					switch(ch){
					case '#':
						tmp[i] = WALL;
						break;
					case 'F':
						tmp[i] = FLAG;
						break;
					case '.':
						tmp[i] = ICE;
						break;
					case ' ':
						tmp[i] = EMPTY;
						break;
					default:
						tmp[i] = EMPTY;
					}
				}
				lines.add(tmp);
			}
			return lines.toArray(new int[lines.size()][]);

		} catch (FileNotFoundException e) {
			e.printStackTrace();
			return null;
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
	}

	public void printBoard(){
		for (int i=0; i< _board.length; i++){
			for (int j=0; j<_board[i].length; j++){
				System.out.print(_board[i][j]);
			}
			System.out.println();
		}
		System.out.println("done");
	}


	public int brickStatus(int x, int y){
		if (x < 0 || y < 0 || x >= _board.length || y >= _board[0].length) return OUTOFBOUNDS;
		return _board[x][y];
	}

	// returns true if cell (x,y) blocks fire
	public boolean isFireBlocked(int x, int y) {
		return _board[x][y] == WALL;
	}
	// returns true if cell (x,y) blocks agent movement
	public boolean isMovementBlocked(int x, int y) {
		if (x < 0 || y < 0 || x >= getHeight() || y >= getWidth()) return true;
		if (_board[x][y] == WALL) return true;
		for (Agent e: _agents){
			if (e.getX()== x && e.getY() == y && ! e.isGhost())  return true;
		}
		return false;
	}

	public boolean isIce(int x, int y) {
		return _board[x][y]== ICE;
	}

	public boolean isFlag(int x, int y) {
		return _board[x][y]== FLAG;
	}

	public int getHeight(){
		return _board.length;
	}

	public int getWidth(){
		return _board[0].length;
	}

	public void removeFlag(int x, int y) {
		_board[x][y] = EMPTY;
	}

	public List<int[]> getFlagList() {
		return _flagList;
	}

	//restores the graffic cell of a map
	public void restore(int x, int y) {
		try{
			switch (_board[x][y]){
			case FLAG:
				_client.sendMessage("Flags " + getCellNumber(x, y));
				break;
			case WALL:
				_client.sendMessage("Walls " + getCellNumber(x, y));
				break;
			case ICE:
				_client.sendMessage("Ice " + getCellNumber(x, y));
				break;
			}
		}catch(IOException e){
			e.printStackTrace();
		}
	}

	public void tryToKill(Agent killer) {
		for (Agent e : _agents){
			if (killer.isKilling(e.getX(), e.getY(), this)){
				e.die();
			}
		}

	}
	// show graphic score
	public void showScore() {
		String score = "Score";
		score = score + " " + _agents.size();
		Object[] agentArr =  _agents.toArray();
		for (int i = 0; i < _agents.size(); i++) {
			score = score + " " + ((Agent) agentArr[i]).getName();
		}
		try {
			_client.sendMessage(score);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void updateScore(Agent agent){
		String update ="UpdateScore";
		update = update + " " + agent.getName();
		update = update + " " + agent.getScore();
		try {
			_client.sendMessage(update);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void runWorld(String[] agentData) {

		for (int i = 0; i < agentData.length; i++) {
			String tmp = agentData[i];
			makeAgent (tmp);
		}

		for (SearchAgent e: _searchAgents){
			for(Automaton a: _sentries){
				e.initSentryLocation(a.getX(), a.getY(), a.getOrientation());
			}
			addAgent(e);
			_queue.add(e);
		}

		for (Automaton a: _sentries){
			addAgent(a);
			_queue.add(a);
		}
		showScore();

		while(!gameOver()){
			try {
				if (!(_queue.peek() instanceof HumanAgent)){
					synchronized (this) {
						Thread.sleep(_delay);
					}
				}
				Agent e = _queue.poll();
				e.makeMove();
				_queue.add(e);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		JOptionPane.showMessageDialog(null, "GAME OVER!!!");
		while(!_queue.isEmpty()){
			Agent e = _queue.poll();
			System.out.println("FINAL SCORE FOR " + e.getType() + " "+ e.getName() + ": " + e.getScore());
			// presenting performance
			if (e.getType().compareToIgnoreCase("searchAgent") == 0
					|| e.getType().compareToIgnoreCase("RTASearchAgent")==0
					|| e.getType().compareToIgnoreCase("GreedySearchAgent")==0 ){

				int T = ((SearchAgent)e).getT();
				int p1 = e.getScore() - T;
				int p2 = e.getScore()*100 - T;
				int p3 = e.getScore()*10000 - T;
				System.out.println("Performance for " + e.getType()+ " " + e.getName() + " with f = 1 is: " + p1);
				System.out.println("Performance for " + e.getType()+ " " + e.getName() + " with f = 100 is: " + p2);
				System.out.println("Performance for " + e.getType()+ " " + e.getName() + " with f = 10000 is: " + p3);}
		}

		System.exit(0);
	}

	private boolean gameOver(){
		boolean allFlagsTaken = true;
		for (int[] flag : _flagList){
			if (brickStatus(flag[0], flag[1])==FLAG){
				allFlagsTaken = false;
				break;
			}
		}
		int numLivingAgents = 0;
		for (Agent e: _agents){
			if (!e.isGhost()) {
				numLivingAgents++;
			}
		}
		return (allFlagsTaken || (numLivingAgents<2));
	}

	private void makeAgent(String tmp) {
		StringTokenizer st = new StringTokenizer(tmp);

		String name = st.nextToken();
		String color = st.nextToken();
		String type = st.nextToken();
		int x =Integer.valueOf(st.nextToken());
		int y =Integer.valueOf(st.nextToken());
		String direction = st.nextToken();

		if (type.equalsIgnoreCase("human")) {
			Agent ha = new HumanAgent(name,color,x,y,this);
			_queue.add(ha);
			addAgent(ha);
		}
		else if (type.equalsIgnoreCase("dumb_Automaton")) {
			_sentries.add(new Automaton(name,color,x,y,this,direction));
		}
		else if (type.equalsIgnoreCase("dumb_Greedy")) {
			DumbGreedyAgent dga = new DumbGreedyAgent(name,color,x,y,this);
			_queue.add(dga);
			addAgent(dga);
		}
		else if (type.equalsIgnoreCase("greedy_Search")) {
			_searchAgents.add(new GreedySearchAgent(name,color,x,y,this));
		}
		else if (type.equalsIgnoreCase("ASearch_Agent")) {
			_searchAgents.add(new SearchAgent(name,color,x,y,this));
		}
		else if (type.equalsIgnoreCase("RealTime_A_Search_Agent")) {
			int maxDepth = Integer.parseInt(st.nextToken());
			_searchAgents.add(new RTASearchAgent(name,color,x,y,this,maxDepth));
		}

	}

	public int getFlagValue() {
		return _flagValues;
	}

}
